binread 2.2.0

A Rust crate for helping read structs from binary data using ✨macro magic✨
Documentation
A Rust crate for helping parse binary data using ✨macro magic✨. # Example ``` # use binread::{prelude::*, io::Cursor, NullString}; #[derive(BinRead)] #[br(magic = b"DOG", assert(name.len() != 0))] struct Dog { bone_pile_count: u8, #[br(big, count = bone_pile_count)] bone_piles: Vec, #[br(align_before = 0xA)] name: NullString } let mut reader = Cursor::new(b"DOG\x02\x00\x01\x00\x12\0\0Rudy\0"); let dog: Dog = reader.read_ne().unwrap(); assert_eq!(dog.bone_piles, &[0x1, 0x12]); assert_eq!(dog.name.into_string(), "Rudy") ``` # The Basics At the core of `binread` is the [`BinRead`](BinRead) trait. It defines how to read a type from bytes and is already implemented for most primitives and simple collections. ```rust use binread::{BinRead, io::Cursor}; let mut reader = Cursor::new(b"\0\0\0\x01"); let val = u32::read(&mut reader).unwrap(); ``` However, [`read`](BinRead::read) is intentionally simple and, as a result, doesn't even allow you to configure the byte order. For that you need [`read_options`](BinRead::read_options) which, while more powerful, isn't exactly ergonomics. So, as a balance between ergonomics and configurability you have the [`BinReaderExt`](BinReaderExt) trait. It is an extension for readers to allow for you to directly read any BinRead types from any reader. Example: ```rust use binread::{BinReaderExt, io::Cursor}; let mut reader = Cursor::new(b"\x00\x0A"); let val: u16 = reader.read_be().unwrap(); assert_eq!(val, 10); ``` It even works for tuples and arrays of BinRead types for up to size 32. # Derive Macro The most significant feature of binread is its ability to use the Derive macro to implement [`BinRead`](BinRead) for your own types. This allows you to replace repetitive imperative code with declarative struct definitions for your binary data parsing. ## Basic Derive Example ```rust # use binread::BinRead; #[derive(BinRead)] struct MyType { first: u32, second: u32 } // Also works with tuple types! #[derive(BinRead)] struct MyType2(u32, u32); ``` ## Attributes The BinRead derive macro uses attributes in order to allow for more complicated parsers. For example you can use `big` or `little` at either the struct-level or the field-level in order to override the byte order of values. ```rust # use binread::{prelude::*, io::Cursor}; #[derive(BinRead)] #[br(little)] struct MyType ( #[br(big)] u32, // will be big endian u32, // will be little endian ); ``` The order of precedence is: (from highest to lowest) 1. Field-level 2. Variant-level (for enums) 3. Top-level 4. Configured (i.e. what endianess was passed in) 5. Native endianess For a list of attributes see the [`attribute`](attribute) module ## Generics The BinRead derive macro also allows for generic parsing. That way you can build up higher-level parsers that can have their type swapped out to allow greater reuse of code. ```rust # use binread::{prelude::*, io::Cursor}; #[derive(BinRead)] struct U32CountVec> { count: u32, #[br(count = count)] data: Vec, } ``` In order to parse generically, we have to (in some way) bound `Args`. The easiest way to do this is to bound `::Args` to `()` (no arguments), however it is also possible to either accept a specific set of arguments or be generic over the given arguments. ## Features * `const_generics` - Change array [`BinRead`] implementation to use const generics * `std` - Disable this feature to enable `no_std` support, on by default